package gov.pbc.nn.core.utils;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.nio.file.DirectoryStream;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.UUID;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * 文件操作的工具类
 * 
 * @author louis
 * 
 */
public class FileUtils extends org.apache.commons.io.FileUtils {

	/**
	 * 创建一个Zip文件系统
	 * 
	 * @param zipPath
	 *            zip文件系统的地址。如果为Null，则取系统默认临时目录。
	 * @return
	 * @throws IOException
	 */
	public static FileSystem createZipFileSystem(Path zipPath) throws IOException {
		if (zipPath == null) {
			zipPath = Files.createTempFile(UUID.randomUUID().toString(), ".zip");
			try (ZipOutputStream out = new ZipOutputStream(new FileOutputStream(zipPath.toFile()))) {
				out.putNextEntry(new ZipEntry(""));
				out.closeEntry();
			}
		}
		URI uri = URI.create("jar:" + zipPath.toFile().toURI());
		Map<String, String> env = new HashMap<>();
		env.put("create", "true");
		FileSystem fs = FileSystems.newFileSystem(uri, env);

		return fs;
	}

	/**
	 * 创建一个临时的Zip文件系统。取系统默认临时目录
	 * 
	 * @return
	 * @throws IOException
	 */
	public static FileSystem createZipFileSystem() throws IOException {
		return createZipFileSystem(null);
	}

	/**
	 * 将多个指定路径的文件（夹）一起压缩
	 * 
	 * @param zipName
	 *            压缩后文件名（不带后缀）
	 * @param destDir
	 *            压缩后的压缩文件存放的目录
	 * @param path
	 *            要压缩的文件路径
	 * @return 压缩文件Path
	 * @throws IOException
	 */
	public static Path compress(String zipName, Path destDir, Path... path) throws IOException {
		if (!Files.isDirectory(destDir)) {
			if (Files.exists(destDir)) {
				throw new UnsupportedOperationException("不是有效的目录！");
			} else {
				Files.createDirectories(destDir);
			}
		}
		Path destPath = destDir.resolve(zipName + ".zip");
		try (FileSystem fs = FileUtils.createZipFileSystem(destPath)) {
			destPath = Paths.get(fs.toString());
			for (Path p : path) {
				if (Files.exists(p)) {
					Path fileName = p.getFileName();
					Path zipPath = fs.getPath("/", fileName.toString());
					Files.copy(p, zipPath, StandardCopyOption.REPLACE_EXISTING);
					FileUtils.compressSubPath(p, zipPath);
				}
			}
		}
		return destPath;
	}

	private static void compressSubPath(Path path, Path zipPath) throws IOException {
		if (Files.isDirectory(path)) {
			try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(path)) {
				for (Path entry : dirStream) {
					Path fileName = entry.getFileName();
					Path subZipPath = zipPath.resolve(fileName.toString());
					Files.copy(entry, subZipPath, StandardCopyOption.REPLACE_EXISTING);
					FileUtils.compressSubPath(entry, subZipPath);
				}
			}
		}
	}

	/**
	 * 解压指定路径的Zip文件到指定位置
	 * 
	 * @param zipPath
	 *            要解压的zip文件路径
	 * @param destPath
	 *            解压后存放路径
	 * @throws IOException
	 */
	public static void uncompress(Path zipPath, Path destPath) throws IOException {
		if (zipPath.toFile().isFile()) {
			if (!Files.exists(destPath)) {
				destPath = Files.createDirectories(destPath);
			}
			try (FileSystem fs = FileUtils.createZipFileSystem(zipPath)) {
				Iterator<Path> iter = fs.getRootDirectories().iterator();
				while (iter.hasNext()) {
					Path source = iter.next();
					FileUtils.uncompressSub(source, destPath);
				}
			}
		} else
			throw new IllegalArgumentException("没有找到相应的文件！");
	}

	private static void uncompressSub(Path zipEntryPath, Path destPath) throws IOException {
		if (Files.isDirectory(zipEntryPath)) {
			try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(zipEntryPath)) {
				for (Path entry : dirStream) {
					Path fileName = entry.getFileName();

					Files.copy(entry, destPath.resolve(fileName.toString()), StandardCopyOption.REPLACE_EXISTING);
					FileUtils.uncompressSub(entry, destPath.resolve(fileName.toString()));
				}
			}
		}
	}

	/**
	 * 将一个文件输入流保存到一个指定的目录中。文件名称是UUID生成的。
	 * 
	 * @param ext
	 *            后缀
	 * @param in
	 * @param dir
	 * @throws IOException
	 */
	public static String copyInputStreamToDirectory(String ext, InputStream in, String dir) throws IOException {
		Path path = Files.createDirectories(Paths.get(dir));
		String fileName = UUID.randomUUID().toString() + "." + ext;
		Path target = path.resolve(fileName);
		Files.copy(in, target);
		return target.toString();
	}

	public static void main(String[] args) throws IOException {
		Path path1 = Paths.get("c:\\新建文本文档.txt");
		Path path2 = Paths.get("c:\\新建文件夹");
		Path dest = Paths.get("c:\\我的测试");
		Path zipFile = FileUtils.compress("测试档案", dest, path1, path2);
		Path destPath = Paths.get("c:\\我的测试");
		FileUtils.uncompress(zipFile, destPath);
	}
}
